<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>画一个三角形</title>
</head>
<body>
    <div class="canvas-wrap">
        <canvas id="myCanvas" width="500" height="500"></canvas>
    </div>

    <script>
        // 顶点 shader，给显卡执行的程序
        const vertexShader = `
            attribute vec3 v3Position;
            void main(void) {
                gl_Position = vec4(v3Position, 1.0);
            }
        `;
    
        // 片元 shader
        const fragmentShader = `
            void main(void) {
                gl_FragColor = vec4(1.0, 1.0, 0.0, 1.0);
            }
        `;

        /**
         * 初始化 vertex shader 和 fragment shader
         * webgl: WebGL 上下文
         * vertexShaderSource: Vertex Shader 的源代码，一般为字符串
         * fragmentShaderSource: Fragment Shader 的源代码，一般为字符串
         */
        function createVFShaderObj (webgl, vertexShaderSource, fragmentShaderSource) {
            // 创建 vertex shader 对象 和 fragment shader 对象
            const vertexShaderObject = webgl.createShader(webgl.VERTEX_SHADER);
            const fragmentShaderObject = webgl.createShader(webgl.FRAGMENT_SHADER);

            // 将 shader 对象与上面创建的 shader 关联起来
            webgl.shaderSource(vertexShaderObject, vertexShaderSource);
            webgl.shaderSource(fragmentShaderObject, fragmentShaderSource);

            // 编译 shader 对象，使之生成显卡可运行的代码
            webgl.compileShader(vertexShaderObject);
            webgl.compileShader(fragmentShaderObject);

            // 如果编译出错，则退出
            if (!webgl.getShaderParameter(vertexShaderObject, webgl.COMPILE_STATUS)) {
                return console.error('Error: 顶点Shader编译出错');
            }
            if (!webgl.getShaderParameter(fragmentShaderObject, webgl.COMPILE_STATUS)) {
                return console.error('Error: 片元Shader编译出错');
            }

            return { vertexShaderObject, fragmentShaderObject };
        }
        
        let webgl = null;
        let vertexShaderObject = null;
        let fragmentShaderObject = null;
        let programObject = null;
        let triangleBuffer = null;
        let v3PositionIndex = 0;

        function init () {
            const canvas = document.querySelector('#myCanvas');
            webgl = canvas.getContext('webgl');

            // 设置 webgl 视口, 与 canvas 大小一致
            webgl.viewport(0, 0, canvas.clientWidth, canvas.clientHeight);

            // 创建 vertext shader 和 fragment shader 对象
            const shaderObj = createVFShaderObj(webgl, vertexShader, fragmentShader);
            vertexShaderObject = shaderObj.vertexShaderObject;
            fragmentShaderObject = shaderObj.fragmentShaderObject;

            // 创建可执行的 webgl 程序
            programObject = webgl.createProgram();

            // 将之前创建的 Shader 对象与 webgl 程序相关联
            webgl.attachShader(programObject, vertexShaderObject);
            webgl.attachShader(programObject, fragmentShaderObject);

            // 绑定 shader 程序中的属性，这里绑定的是 vertexShader 中 v3Position 属性
            webgl.bindAttribLocation(programObject, v3PositionIndex, 'v3Position');

            // 链接程序
            webgl.linkProgram(programObject);
            // 如果链接程序出错，则报错退出
            if (!webgl.getProgramParameter(programObject, webgl.LINK_STATUS)) {
                return console.error('Error: 程序链接出错');
            }

            // 使用程序
            webgl.useProgram(programObject);

            const jsArrayData = [
                0.0, 1.0, 0.0,
                -1.0, -1.0, 0.0,
                1.0, 1.0, 0.0
            ];

            // 创建 Buffer
            triangleBuffer = webgl.createBuffer();

            // 绑定 Buffer
            webgl.bindBuffer(webgl.ARRAY_BUFFER, triangleBuffer);
            // 设置 Buffer 的数据
            // webgl.bufferData(webgl.ARRAY_BUFFER, new Float32Array(jsArrayData), webgl.STATIC_DRAW);

            // 设置 Buffer 数据的函数
            // void bufferData(GLenum target, GLsizeiptr size, GLenum usage);
            // void bufferData(GLenum target, [AllowShared] BufferSource? data, GLenum usage);
            // void bufferSubData(GLenum target, GLintptr offset, [AllowShared] BufferSource data);
            webgl.bufferData(webgl.ARRAY_BUFFER, 4 * 9, webgl.STATIC_DRAW);     // 4 * 9 是指有 9 个数据，浮点数的字节是 4
            // webgl.bufferSubData(webgl.ARRAY_BUFFER, 0, new Float32Array(jsArrayData));            // 在 offset = 0, 设置数据。 offset 指偏移的字节数
            webgl.bufferSubData(webgl.ARRAY_BUFFER, 0, new Float32Array([-1.0, 0.0, 0.0]));            // 在 offset = 0, 设置数据。 offset 指偏移的字节数
            webgl.bufferSubData(webgl.ARRAY_BUFFER, 4 * 3, new Float32Array([1.0, 0.0, 0.0]));            // 在 offset = 4 * 3, 设置数据。 offset 指偏移的字节数
            webgl.bufferSubData(webgl.ARRAY_BUFFER, 4 * 6, new Float32Array([0.0, -1.0, 0.0]));            // 在 offset = 4 * 6, 设置数据。 offset 指偏移的字节数

            // bufferData 的三种类型
            // STATIC_DRAW 静态，一直不变
            // DYNAMIC_DRAW 动态，经常变
            // STREAM_DRAW  流，偶尔变

            // 设置颜色缓冲区为黑色
            webgl.clearColor(0.0, 0.0, 0.0, 1.0);
            webgl.clear(webgl.COLOR_BUFFER_BIT);

            // 使用 Buffer，其实设置 ARRAY_BUFFER 当前状态
            webgl.bindBuffer(webgl.ARRAY_BUFFER, triangleBuffer);
            // webgl.bindBuffer(webgl.ARRAY_BUFFER, 0);        // 不使用任何缓冲区


            //
            webgl.enableVertexAttribArray(v3PositionIndex);

            webgl.vertexAttribPointer(v3PositionIndex, 3, webgl.FLOAT, false, 0, 0);

            webgl.drawArrays(webgl.TRIANGLES, 0, 3);
        }

        init();

    </script>
</body>
</html>